//	MIT License
//
//	Copyright (c) 2020 LiuJ
//
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in all
//	copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//	SOFTWARE.
//

package service

import (
	"go.uber.org/atomic"
	"sync"
	"time"
)

var (
	initTaskNum_ = 0
)

// Task...
type Task struct {
	tick *time.Ticker
	do func() error
	period uint64
}

// Scheduler...
type Scheduler struct {
	tasks []*Task
	taskNum atomic.Uint64
	opMu  sync.Mutex
	closeCh chan interface{}
}

// NewScheduler...
func NewScheduler() *Scheduler {
	return &Scheduler{tasks: make([]*Task, initTaskNum_)}
}

// AddTask...
func (s *Scheduler) AddTask(period uint64, run func() error) {
	s.opMu.Lock()
	defer s.opMu.Unlock()
	s.tasks = append(s.tasks, &Task{do: run, period: period, tick: time.NewTicker(time.Duration(period) * time.Millisecond)})
	s.taskNum.Inc()
}

// RunAllTasks...
func (s *Scheduler) RunAllTasks() {
	s.closeCh = make(chan interface{})
	for _, task := range s.tasks {
		go func(tk *Task) {
			for {
				select {
				case <-s.closeCh:
					return
				case <-tk.tick.C:
					if err := tk.do(); err != nil {
						return
					}
				}
			}
		}(task)
	}
}

// StopAllTasks...
func (s *Scheduler) StopAllTasks() {
	close(s.closeCh)
	for _, task := range s.tasks {
		go func(tk *Task) {
			tk.tick.Stop()
		}(task)
	}
}
